#include "config.h"

#include "ebc_p.h"

static ebc_err_t
pch_write(ebc_t *ec, const ecm_uint8 *data, ecm_size_t size)
{
	return PCH_WRITE(ec->default_pchp, (char *)data, &size, ECM_TRUE);
}

ebc_err_t
ebc_peer_send(ecm_ebc_t ecm_ebc, const ecm_uint8 *data, ecm_size_t size)
{
	ebc_t	*ec = (ebc_t *)ecm_ebc;
	ecm_size_t	datasize;
	ebc_err_t	err;

	if (ecm_ebc == NULL || (size > 0 && data == NULL))
		return -ERR_EBC_INVARG;

	if (ec->snr_shutdown)
		return -ERR_EBC_GENERAL;

	datasize = ecm_htonl(size);
	if ((err = pch_write(ec, (ecm_uint8 *)&datasize, (ecm_size_t)sizeof(ecm_size_t))) != EBC_OK)
		return err;
	if (size == 0)
		return EBC_OK;
	return pch_write(ec, data, size);
}

static ebc_err_t
pch_read(ebc_pch_t *pchp, ecm_uint8 *pdata, ecm_size_t size, ecm_uint32 *ptimeout)
{
	pch_addr_t	pchaddr;
	ecm_size_t	buflen = size;
	ecm_size_t	ptr = 0;
	ebc_err_t	err;

again:
	err = PCH_READ(pchp, &pchaddr, (char *)(pdata + ptr), &buflen, ptimeout);
	if (err != EBC_OK)
		return err;
	ptr += buflen;
	if (ptr < size) {
		buflen = size - ptr;
		goto again;
	}
	return EBC_OK;
}

static ebc_err_t
pch_read_size(ebc_pch_t *pchp, ecm_size_t *psize, ecm_uint32 *ptimeout)
{
	ecm_size_t	size;
	ebc_err_t	err;

	if ((err = pch_read(pchp, (ecm_uint8 *)&size, sizeof(ecm_size_t), ptimeout)) != EBC_OK)
		return err;
	*psize = ecm_ntohl(size);
	return EBC_OK;
}

ebc_err_t
ebc_peer_arecv(ecm_ebc_t ecm_ebc, ecm_uint8 **pdata, ecm_size_t *psize, ecm_uint32 *ptimeout)
{
	ebc_t	*ec = (ebc_t *)ecm_ebc;
	ebc_pch_t	*pchp;
	ecm_uint8	*data;
	ecm_size_t	size;
	ebc_err_t	err;

	if (ecm_ebc == NULL || psize == NULL || pdata == NULL)
		return -ERR_EBC_INVARG;

	if (ec->snr_shutdown)
		return -ERR_EBC_GENERAL;

	pchp = ec->default_pchp;
	if ((err = pch_read_size(pchp, &size, ptimeout)) != EBC_OK)
		goto out;

	if (size > 0) {
		data = ecm_malloc(size);
		if (data == NULL)
			return -ERR_EBC_GENERAL;
		if ((err = pch_read(pchp, data, size, ptimeout)) != EBC_OK) {
			ecm_free(data);
			goto out;
		}
		*pdata = data;
	}
	else
		*pdata = NULL;
	*psize = size;
out:
	if (err != EBC_OK) {
		if (err == -ERR_EBC_AGAIN)
			return -ERR_EBC_TIMEOUT;
		return err;
	}
	return EBC_OK;
}

ebc_err_t
ebc_peer_recv(ecm_ebc_t ecm_ebc, ecm_uint8 *data, ecm_size_t *psize, ecm_uint32 *ptimeout)
{
	ebc_t	*ec = (ebc_t *)ecm_ebc;
	ebc_pch_t	*pchp;
	ecm_size_t	size;
	ebc_err_t	err;

	if (ecm_ebc == NULL || psize == NULL || (*psize > 0 && data == NULL))
		return -ERR_EBC_INVARG;

	if (ec->snr_shutdown)
		return -ERR_EBC_GENERAL;

	pchp = ec->default_pchp;
	if (pchp->n_recvs == 0) {
		if ((err = pch_read_size(pchp, &pchp->n_recvs, ptimeout)) != EBC_OK)
			goto out;
		if (pchp->n_recvs == 0) {
			*psize = 0;
			return EBC_OK;
		}
	}
	if (pchp->n_recvs > *psize)
		size = *psize;
	else
		size = pchp->n_recvs;

	if ((err = pch_read(pchp, data, size, ptimeout)) != EBC_OK)
		goto out;
	*psize = size;
	pchp->n_recvs -= size;
out:
	if (err != EBC_OK) {
		if (err == -ERR_EBC_AGAIN)
			return -ERR_EBC_TIMEOUT;
		return err;
	}
	return EBC_OK;
}
